Proof Key for Code Exchange (PKCE) is a mechanism, typically used together with an OAuth2 Authorization Code Grant flow to provide an enhanced level of security when authenticating to an Identity Provider (IDP) to get an access token. In fact for Single Page Applications (SPA), Authorization Code Grant flow with PKCE is now the recommended OAuth2 authentication protocol over its predecessor, the Implicit Grant flow, for acquiring an access token. For other client types, including Native and Confidential, that use Authorization Code Grant flow, it’s still recommended use this flow with PKCE if possible.
Let’s talk about the protocol…
I’ll assume that you already have an understanding of how OAuth2 Authorization Code Grant works, so I won’t cover that in great detail. I’ll attempt to simplify how PKCE can be used on top of Authorization Code grant to make the protocol more secure with the following diagram:
Everything in the above diagram except for the red addition for PKCE is how the Authorization Code grant flow works.
- In Step 1, the client application creates a “secret” string, called a “Code Verifier”. It then uses an algorithm to hash this secret string and then sends the hash of this secret string known as the “Code Challenge” in the Authentication request. The currently supported hashing algorithm are:
- plain – there is no hashing. The secret is sent as is
- S256 – The secret is hashed using SHA256 Algorithm
- Upon verifying the credentials, Azure AD sends back to the client an Authorization Code
- The client redeems the Authorization Code for an Access Token. In this step the client also needs to send the “secret” string aka the “Code Verifier”
- Azure AD verifies that the “secret” string’s hash matches what it receives in step 1 and issues a token
PKCE helps mitigate the Authorization Code interception security attack which can happen in step 2 where a malicious app can intercept the Authorization Code. Without knowing the “secret” the malicious app won’t be able to exchange the Authorization Code for an access token. Without a valid “code verifier”, Azure AD may return the following error:
AADSTS50148: The code_verifier does not match the code_challenge supplied in the authorization request for PKCE
Let’s use PostMan to perform this flow
You need to have at least PostMan version 7.23 installed and a registered application in Azure AD. If you want to create a new App Registration for this, you can create a new app with the following setting:
- Use ‘Accounts in this organizational directory only – Single tenant’ for supported account types.
- Add ‘https://localhost’ as a Web Platform redirect URI
- Create a secret in the ‘Certificates & secrets’ blade – take note of the secret as you won’t be able to see this secret once navigating away from the pane.
- Take note of the Application (client) ID of the app in the Overview blade
Configuring PostMan…
Launch PostMan and click on the ‘Authorization’ section. Choose ‘OAuth 2.0’ in the drop down under Type. Click on ‘Get New Access Token’ button.
In the Get New Access Token dialog:
- For Grant Type, choose ‘Authorization Code (With PKCE)’ from the drop down
- Callback URL – this is the redirect URL configured earlier in the App Registration, so use ‘https://localhost’
- For Auth URL, use ‘https://login.microsoftonline.com/<Directory ID>/oauth2/v2.0/authorize’
- For Access Token URL, use ‘https://login.microsoftonline.com/<Directory ID>/oauth2/v2.0/token’
- Code Challenge Method – use SHA-256
- Code Verifier – you can leave blank and PostMan will generate one for you. Alternatively you can use your own secret string or use this Online PKCE Generator Tool to create one for you. Note that PostMan will automatically create a “Code Challenge” based on the supplied “Code Verifier”.
- Fill in the Client ID and Client Secret info from your App Registration
- You can use ‘https://graph.microsoft.com/.default’ for scope. When creating a new App Registration in the portal, the Microsoft Graph permission “User.Read” should already be configured.
Here is what things should look like in PostMan:
Clicking on “Request Token” should prompt you for login and returning an access token.
Doing it manually…
- Get the Authorization Code:
GET https://login.microsoftonline.com/<tenant>/oauth2/v2.0/authorize?
response_type=code
&client_id=<your App ID>
&scope=openid
&redirect_uri=https%3A%2F%2Flocalhost
&code_challenge=<your code challenge>
&code_challenge_method=S256
This step can be tried by using a web browser. Paste in the URL above with your parameter and authenticate. Once you successfully authenticate, you can get the Authorization Code from the the browser’s address bar
- Exchange the Authorization Code for an Access Token
POST https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token
POST Request Body:
grant_type : authorization_code
code : <Authorization Code from step 1>
redirect_uri : https://localhost
client_id : <your App ID>
client_secret : <your App Secret>
code_verifier : <your code verifier>
You can try this step in either PostMan or Fiddler. Note that if you use ‘plain’ for ‘code_challenge_method’ in step 1 then the ‘code_challenge’ is the same as the ‘code_verifier’.
The Code Verifier
The PKCE specification requires that the Code Verifier be a random string of these characters: {[A-Z] / [a-z] / [0-9] / “-” / “.” / “_” / “~”} and that its length be between 43 characters and 128 characters. At the time of this writing, Azure AD does not seem to impose the length limit.
The Code Challenge
Per PKCE specification, the Code Challenge is defined as below:
For S256 transformation, the Code Challenge is the Base64URL-Encoding of the SHA256 hash of the Code Verifier.
Borrowing the code snippet from https://medium.com/the-new-control-plane/using-proof-key-for-code-exchange-pkce-in-adfs-for-windows-server-2019-a457172e28c3, I made some modification to generate a random “Code Verifier” string. Below is the .Net code to create both a Code Verifier and Code Challenge:
using IdentityModel; using System; using System.Linq; using System.Security.Cryptography; using System.Text; namespace PKCEConsoleApp2 { class Program { static void Main(string[] args) { var random = new Random(); int cvlength = random.Next(43, 128); const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"; var codeVerifier = new string (Enumerable.Repeat(chars, cvlength).Select(s => s[random.Next(s.Length)]).ToArray()); string codeChallenge; using (var sha256 = SHA256.Create()) { var a = Encoding.UTF8.GetBytes(codeVerifier); var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier)); codeChallenge = Base64Url.Encode(challengeBytes); /* Alternatively instead of using Base64Url.Encode method, the code below can accomplish the same result: var result = Convert.ToBase64String(challengeBytes) .Replace('+', '-') // replace URL unsafe characters with safe ones .Replace('/', '_') // replace URL unsafe characters with safe ones .Replace("=", ""); // no padding */ } Console.WriteLine("codeVerifier " + codeVerifier + "\n"); Console.WriteLine("codeChallenge " + codeChallenge + "\n"); Console.ReadLine(); } } }
For nodejs application, take a look at this npm package, and to see how to do it in javascript, refer to https://stackoverflow.com/questions/59777670/how-can-i-hash-a-string-with-sha256-in-js for some ideas.
Single Page Application (SPA) Apps
We recommend using MSAL.js v2 for performing Authorization Code Grant with PKCE for Azure AD Applications. Take a look at this tutorial and the code sample at https://github.com/Azure-Samples/ms-identity-javascript-v2 for more info.
Note that MSAL.js v1 is for Applications performing Implicit Grant flow and MSAL.Js v2 is for Authorization Code flow with PKCE.
Intelligent Tracking Protection (ITP) feature
Due to privacy concern, certain browsers, for instance Safari, may implement ITP feature to block 3rd party cookies. This results in breaking authentication for SPA apps using Implicit Grant flow. For this, it’s recommended that you use Authorization Code flow with PKCE for authentication. Refer to https://docs.microsoft.com/en-us/azure/active-directory/develop/reference-third-party-cookies-spas for more info.
Man, thanks for this. I had to update a few things and I added the updates to StackOverflow @ https://stackoverflow.com/questions/47275079/request-access-token-in-postman-for-azure-ad-b2c/67825027#67825027
Glad it helped
Hi, i need a help in this, when i am putting {ScopeValue }as scope defined in my webapi client in AAD i am not getting valid access token i am only getting access token in postman if i put MS graph scope values in scope parameter, and due to which i am not able to authorize my web api using the access token i am getting.
I don’t think that will work with just scope name. You will need to provide a fully qualified scope URL, which should be in this format: [App ID URI]/[scope name]
Hey! Thanks for this valuable info. There’s something I don’t totally understand…
Why we need to create a secret? We cannot simulate a SPA flow in postman? The sample SPA MSAL PKCE does not use a secret.
Thanks a lot!
For true SPA app, it’s treated as a public client type when registering the redirect URI. PostMan in this case is being used in a Confidential client type manner hence the secret is required (note fore redirect URI I chose ‘web’ platform instead of Single Page Application type as a real SPA app sample would use)